// see 'typeRelatedToDiscriminatedType' in checker.ts:

// IteratorResult
namespace Example1 {
    type S = { done: boolean, value: number };
    type T =
        | { done: true, value: number }     // T0
        | { done: false, value: number };   // T1

    declare let s: S;
    declare let t: T;

    // S is assignable to T0 when S["done"] is true
    // S is assignable to T1 when S["done"] is false
    t = s;
}

// Dropping constituents of T
namespace Example2 {
    type S = { a: 0 | 2, b: 4 };
    type T = { a: 0,     b: 1 | 4 }     // T0
           | { a: 1,     b: 2 }         // T1
           | { a: 2,     b: 3 | 4 };    // T2
    declare let s: S;
    declare let t: T;

    // S is assignable to T0 when S["a"] is 0
    // S is assignable to T2 when S["a"] is 2
    t = s;
}

// Unmatched discriminants
namespace Example3 {
    type S = { a: 0 | 2, b: 4 };
    type T = { a: 0,     b: 1 | 4 }     // T0
           | { a: 1,     b: 2 | 4 }     // T1
           | { a: 2,     b: 3 };        // T2
    declare let s: S;
    declare let t: T;

    // S is assignable to T0 when S["a"] is 0
    // S is *not* assignable to T1 when S["b"] is 4
    // S is *not* assignable to T2 when S["a"] is 2
    t = s;
}

// Unmatched non-discriminants
namespace Example4 {
    type S = { a: 0 | 2, b: 4 };
    type T = { a: 0,     b: 1 | 4 }             // T0
           | { a: 1,     b: 2 }                 // T1
           | { a: 2,     b: 3 | 4, c: string }; // T2
    declare let s: S;
    declare let t: T;

    // S is assignable to T0 when S["a"] is 0
    // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c"
    t = s;
}

// Maximum discriminant combinations
namespace Example5 {
    // NOTE: The maximum number of discriminant type combinations is currently 25.
    //       3 discriminant properties with 3 types a piece
    //       is 27 possible combinations.
    type N = 0 | 1 | 2;
    type S = { a: N, b: N, c: N };
    type T = { a: 0, b: N, c: N }
           | { a: 1, b: N, c: N }
           | { a: 2, b: N, c: N }
           | { a: N, b: 0, c: N }
           | { a: N, b: 1, c: N }
           | { a: N, b: 2, c: N }
           | { a: N, b: N, c: 0 }
           | { a: N, b: N, c: 1 }
           | { a: N, b: N, c: 2 };
    declare let s: S;
    declare let t: T;

    // S *should* be assignable but the number of
    // combinations is too complex.
    t = s;
}

// https://github.com/Microsoft/TypeScript/issues/14865
namespace GH14865 {
    type Style1 = {
        type: "A";
        data: string;
    } | {
        type: "B";
        data: string;
    };

    type Style2 = {
        type: "A" | "B";
        data: string;
    }

    const a: Style2 = { type: "A", data: "whatevs" };
    let b: Style1;
    a.type; // "A" | "B"
    b.type; // "A" | "B"
    b = a; // should be assignable
}

// https://github.com/Microsoft/TypeScript/issues/30170
namespace GH30170 {
    interface Blue {
        color: 'blue'
    }
    interface Yellow {
        color?: 'yellow',
    }
    function draw(val: Blue | Yellow) { }

    function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) {
        return draw({ color: currentColor });
    }
}

// https://github.com/Microsoft/TypeScript/issues/12052
namespace GH12052 {
    interface ILinearAxis { type: "linear"; }

    interface ICategoricalAxis { type: "categorical"; }

    type IAxis = ILinearAxis | ICategoricalAxis;
    type IAxisType = "linear" | "categorical";

    function getAxisType(): IAxisType {
        if (1 == 1) {
            return "categorical";
        } else {
            return "linear";
        }
    }

    const bad: IAxis = { type: getAxisType() };
    const good: IAxis = { type: undefined };
    good.type = getAxisType();
}

// https://github.com/Microsoft/TypeScript/issues/18421
namespace GH18421 {
    interface ThingTypeOne {
        type: 'one';
    }

    interface ThingTypeTwo {
        type: 'two';
    }

    type ThingType = 'one' | 'two';

    type Thing = ThingTypeOne | ThingTypeTwo;

    function makeNewThing(thingType: ThingType): Thing {
        return {
            type: thingType
        };
    }
}

// https://github.com/Microsoft/TypeScript/issues/15907
namespace GH15907 {
    type Action = { type: 'activate' } | { type: 'disactivate' };

    function dispatchAction(action: Action): void {

    }

    const active = true;

    dispatchAction({ type : (active? 'disactivate' : 'activate') });
}

// https://github.com/Microsoft/TypeScript/issues/20889
namespace GH20889 {
    interface A1 {
        type: "A1";
    }
    interface A2 {
        type: "A2";
    }
    type AU = A1 | A2;

    function foo(obj1: AU) {
        const obj2: AU = {
            type: obj1.type
        };
    }
}

// https://github.com/microsoft/TypeScript/issues/39357
namespace GH39357 {
    type A = ["a", number] | ["b", number] | ["c", string];
    type B = "a" | "b" | "c";
    declare const b: B;
    const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
}
